﻿#if DEBUG
//#define USE_BENCHMARK
#if USE_BENCHMARK
namespace Test {

    using LiteByte.Common;
    using LiteByte.Converters;
    using System;
    using System.Text;
    using System.Text.Json;
    using System.Diagnostics;
    using System.Collections.Generic;
    using BenchmarkDotNet.Attributes;

    [ShortRunJob]
    [MemoryDiagnoser]
    public class LBBenckmarks {

        private ClassTestVO classTestVO;

        [GlobalSetup]
        public void Setup() {
            LoadClasses();
            classTestVO = new ClassTestVO() {
                boolValue = true,
                charValue = 'a',
                byteValue = byte.MaxValue,
                sbyteValue = sbyte.MinValue,
                shortValue = short.MinValue,
                ushortValue = ushort.MaxValue,
                intValue = 10086,
                uintValue = 68001,
                longValue = long.MinValue,
                ulongValue = ulong.MaxValue,
                floatValue = -123.456f,
                doubleValue = 12345.1234567890,
                decimalValue = -1234567890.0123456789m,
                dateTime = DateTime.Now,
                stringValue = "asdf1234",
                array = new int[] { 1, 2, 3, 4, 5 },
                list = new List<int>() { 10, 11, 12, 13, 14 },
                map = new Dictionary<string, string>() { { "a", "ABC" }, { "b", "BCD" }, { "c", "CDE" } },
                classObject = new IDCard("123456YYYYMMDD123X", "张三", 18, 1, "地球村"),
                structObject = new Position(1.1f, 1.2f, 1.3f),
                emptyObject = new EmptyVO()
            };
        }

        [Params(10, 100)] // 测试不同规模的数据
        public int DataSize { get; set; }

        [Benchmark]
        public void SerializeClassTestVO() {
            byte[] bytes = LBUtil.Serialize(classTestVO, "ClassTestVO");
        }

        //[Benchmark]
        public void DeserializeClassTestVO() {
            //ClassTestVO vo = LBUtil.Deserialize<ClassTestVO>(classTestVO, "ClassTestVO");
        }

        public void TestAllTypes() {
            ClassTest();
            StructTest();
            ArrayTest();
            ListTest();
            DictionaryTest();
            //NestedTest();
            BaseTypeTest();
        }

        /// <summary> 加载自定义类 </summary>
        private void LoadClasses() {
            string root = "../../../../Resources/LBTypes/";
            string[] files = {
                "ClassTestVO.txt",
                "StructTestVO.txt",
                "ArrayTestVO.txt",
                "ListTestVO.txt",
                "DictionaryTestVO.txt",
                "UserVO.txt",
                "ColorVO.txt",
                "Vector3VO.txt",
            };
            string[] codes = new string[files.Length];
            for (int i = 0; i < files.Length; i++) {
                string url = root + files[i];
                string text = File.ReadAllText(url, Encoding.UTF8);
                codes[i] = text;
            }
            LBUtil.LoadClasses(codes);
        }

        #region Test
        private JsonSerializerOptions jsonOptions = new JsonSerializerOptions() { IncludeFields = true, WriteIndented = false };

        private void ClassTest() {
            // Serialize
            ClassTestVO vo = new ClassTestVO() {
                boolValue = true,
                charValue = 'a',
                byteValue = byte.MaxValue,
                sbyteValue = sbyte.MinValue,
                shortValue = short.MinValue,
                ushortValue = ushort.MaxValue,
                intValue = 10086,
                uintValue = 68001,
                longValue = long.MinValue,
                ulongValue = ulong.MaxValue,
                floatValue = -123.456f,
                doubleValue = 12345.1234567890,
                decimalValue = -1234567890.0123456789m,
                dateTime = DateTime.Now,
                stringValue = "asdf1234",
                array = new int[] { 1, 2, 3, 4, 5 },
                list = new List<int>() { 10, 11, 12, 13, 14 },
                map = new Dictionary<string, string>() { { "a", "ABC" }, { "b", "BCD" }, { "c", "CDE" } },
                classObject = new IDCard("123456YYYYMMDD123X", "张三", 18, 1, "地球村"),
                structObject = new Position(1.1f, 1.2f, 1.3f),
                emptyObject = new EmptyVO()
            };
            byte[] bytes = LBUtil.Serialize(vo, "ClassTestVO");

            // Deserialize
            ClassTestVO _vo = LBUtil.Deserialize<ClassTestVO>(bytes, "ClassTestVO");
            string json = JsonSerializer.Serialize(_vo, jsonOptions);
            Trace.WriteLine($"ClassTest() LBSize:{bytes.Length} JsonSize:{json.Length} json:\n{json}");
        }

        private void StructTest() {
            StructTestVO vo1 = new StructTestVO() {
                p = new Vector3VO(1.23f, 2.34f, 3.45f),
                color = new ColorVO(1 / 255f, 2 / 255f, 3 / 255f)
            };
            StructTestVO vo2 = new StructTestVO() { };
            byte[] bytes1 = LBUtil.Serialize(vo1, "StructTestVO");
            byte[] bytes2 = LBUtil.Serialize(vo2, "StructTestVO");

            StructTestVO _vo1 = LBUtil.Deserialize<StructTestVO>(bytes1, "StructTestVO");
            StructTestVO _vo2 = LBUtil.Deserialize<StructTestVO>(bytes2, "StructTestVO");
            string json1 = JsonSerializer.Serialize(vo1, jsonOptions);
            string json2 = JsonSerializer.Serialize(vo2, jsonOptions);
            Trace.WriteLine($"StructTest() LBSize:{bytes1.Length} JsonSize:{json1.Length}");
            Trace.WriteLine($"StructTest() LBSize:{bytes2.Length} JsonSize:{json2.Length}");
        }

        private void ArrayTest() {
            ArrayTestVO vo = new ArrayTestVO() {
                bit1s = new bool[] { true, false, true, false },
                chars = new char[] { 'A', 'B', 'C', 'D' },
                int8s = new sbyte[] { 1, 2, 0, sbyte.MinValue, sbyte.MaxValue },
                int16s = new short[] { 1, 2, 0, short.MinValue, short.MaxValue },
                int32s = new int[] { 1, 2, 0, int.MinValue, int.MaxValue },
                int64s = new long[] { 100L, 200L, 0L, long.MinValue, long.MaxValue },
                uint8s = new byte[] { 1, 2, 0, byte.MinValue, byte.MaxValue },
                uint16s = new ushort[] { 1, 2, 0, ushort.MinValue, ushort.MaxValue },
                uint32s = new uint[] { 1, 2, 0, uint.MinValue, uint.MaxValue },
                uint64s = new ulong[] { 100L, 200L, 0L, ulong.MinValue, ulong.MaxValue },
                float8s = new float[] { 1f / 255f, 2 / 255f, 3f / 255f, LBFloat8.MinValue, LBFloat8.MaxValue },
                float16s = new float[] { 1.23f, float.PositiveInfinity, float.NegativeInfinity, float.NaN, 0f, -0f },
                float32s = new float[] { 123.456f, float.MinValue, float.MaxValue, float.PositiveInfinity, float.NegativeInfinity, float.NaN, 0f, -0f },
                float64s = new double[] { 1234567.1234567d, double.MinValue, double.MaxValue, double.PositiveInfinity, double.NegativeInfinity, float.NaN, 0f, -0f },
                decimals = new decimal[] { 1234567890.0123456789m, -9876543210.0123456789m, decimal.MinValue, decimal.MaxValue },
                dateTimes = new DateTime[] { DateTime.Now, default(DateTime) },
                vint16s = new short[] { 1, 2, 0, short.MinValue, short.MaxValue },
                vint32s = new int[] { 1, 2, 0, int.MinValue, int.MaxValue },
                vint64s = new long[] { 100L, 200L, 0L, long.MinValue, long.MaxValue },
                vuint16s = new ushort[] { 1, 2, 0, ushort.MinValue, ushort.MaxValue },
                vuint32s = new uint[] { 1, 2, 0, uint.MinValue, uint.MaxValue },
                vuint64s = new ulong[] { 100L, 200L, 0L, ulong.MinValue, ulong.MaxValue },
                asciis = new string[] { "a", "b", "c", "d", "e", null },
                utf8s = new string[] { "utf8_1", "utf8_2", "张三", char.ConvertFromUtf32(0x1F600), char.ConvertFromUtf32(0x1f602), null },
                utf16s = new string[] { "utf16_1", "utf16_2", "李四", char.ConvertFromUtf32(0x1F600), char.ConvertFromUtf32(0x1f602), null },
                utf32s = new string[] { "utf32_1", "utf32_2", "王五", char.ConvertFromUtf32(0x1F600), char.ConvertFromUtf32(0x1f602), null },
                vunicodes = new string[] { "vunicode_1", "vunicode_2", "赵六", char.ConvertFromUtf32(0x1F600), char.ConvertFromUtf32(0x1f602), null },
                classes = new UserVO[] {
                    new UserVO(0, "user0"),
                    new UserVO(1, "user1"),
                    new UserVO(2, "user2"),
                    null
                },
                structs = new Vector3VO[] { new Vector3VO(0f, 0f, 0f), new Vector3VO(1f, 2f, 3f), new Vector3VO(4f, 5f, 6f) }
            };
            byte[] bytes = LBUtil.Serialize(vo, "ArrayTestVO");
            ArrayTestVO _vo = LBUtil.Deserialize<ArrayTestVO>(bytes, "ArrayTestVO");
            //string json = JsonSerializer.Serialize(_vo, jsonOptions);
            //Trace.WriteLine($"ArrayTestVO() LBSize:{bytes.Length} JsonSize:{json.Length} obj:{json}");
            Trace.WriteLine($"ArrayTestVO() LBSize:{bytes.Length}");
        }

        private void ListTest() {
            ListTestVO vo = new ListTestVO() {
                bit1s = new List<bool>() { true, false, true, false },
                chars = new List<char>() { 'A', 'B', 'C', 'D' },
                int8s = new List<sbyte>() { 1, 2, 0, sbyte.MinValue, sbyte.MaxValue },
                int16s = new List<short>() { 1, 2, 0, short.MinValue, short.MaxValue },
                int32s = new List<int>() { 1, 2, 0, int.MinValue, int.MaxValue },
                int64s = new List<long>() { 100L, 200L, 0L, long.MinValue, long.MaxValue },
                uint8s = new List<byte>() { 1, 2, 0, byte.MinValue, byte.MaxValue },
                uint16s = new List<ushort>() { 1, 2, 0, ushort.MinValue, ushort.MaxValue },
                uint32s = new List<uint>() { 1, 2, 0, uint.MinValue, uint.MaxValue },
                uint64s = new List<ulong>() { 100L, 200L, 0L, ulong.MinValue, ulong.MaxValue },
                float8s = new List<float>() { 1f / 255f, 2 / 255f, 3f / 255f, LBFloat8.MinValue, LBFloat8.MaxValue },
                float16s = new List<float>() { 1.23f, float.PositiveInfinity, float.NegativeInfinity, float.NaN, 0f, -0f },
                float32s = new List<float>() { 123.456f, float.MinValue, float.MaxValue, float.PositiveInfinity, float.NegativeInfinity, float.NaN, 0f, -0f },
                float64s = new List<double>() { 1234567.1234567d, double.MinValue, double.MaxValue, double.PositiveInfinity, double.NegativeInfinity, float.NaN, 0f, -0f },
                decimals = new List<decimal>() { 1234567890.0123456789m, -9876543210.0123456789m, decimal.MinValue, decimal.MaxValue },
                dateTimes = new List<DateTime>() { DateTime.Now, default(DateTime) },
                vint16s = new List<short>() { 1, 2, 0, short.MinValue, short.MaxValue },
                vint32s = new List<int>() { 1, 2, 0, int.MinValue, int.MaxValue },
                vint64s = new List<long>() { 100L, 200L, 0L, long.MinValue, long.MaxValue },
                vuint16s = new List<ushort>() { 1, 2, 0, ushort.MinValue, ushort.MaxValue },
                vuint32s = new List<uint>() { 1, 2, 0, uint.MinValue, uint.MaxValue },
                vuint64s = new List<ulong>() { 100L, 200L, 0L, ulong.MinValue, ulong.MaxValue },
                asciis = new List<string>() { "a", "b", "c", "d", "e", null },
                utf8s = new List<string>() { "utf8_1", "utf8_2", "张三", char.ConvertFromUtf32(0x1F600), char.ConvertFromUtf32(0x1f602), null },
                utf16s = new List<string>() { "utf16_1", "utf16_2", "李四", char.ConvertFromUtf32(0x1F600), char.ConvertFromUtf32(0x1f602), null },
                utf32s = new List<string>() { "utf32_1", "utf32_2", "王五", char.ConvertFromUtf32(0x1F600), char.ConvertFromUtf32(0x1f602), null },
                vunicodes = new List<string>() { "vunicode_1", "vunicode_2", "赵六", char.ConvertFromUtf32(0x1F600), char.ConvertFromUtf32(0x1f602), null },
                classes = new List<UserVO>() {
                    new UserVO(0, "user0"),
                    new UserVO(1, "user1"),
                    new UserVO(2, "user2"),
                    null
                },
                structs = new List<Vector3VO>() { new Vector3VO(0f, 0f, 0f), new Vector3VO(1f, 2f, 3f), new Vector3VO(4f, 5f, 6f) }
            };
            byte[] bytes = LBUtil.Serialize(vo, "ListTestVO");
            ListTestVO _vo = LBUtil.Deserialize<ListTestVO>(bytes, "ListTestVO");
            Trace.WriteLine($"ListTest() LBSize:{bytes.Length}");
        }

        private void DictionaryTest() {
            DictionaryTestVO vo = new DictionaryTestVO() {
                nullMap = null,
                emptyMap = new Dictionary<string, string>(),
                int_intMap = new Dictionary<int, int>() {
                    { 1, 10 },
                    { 2, 20 },
                    { 3, 30 }
                },
                str_strMap = new Dictionary<string, string>() {
                    { "a", "A" },
                    { "b", "B" },
                    { "c", "C" }
                },
                str_intArrayMap = new Dictionary<string, int[]>() {
                    { "a", new int[] { 1, 2, 3 } },
                    { "b", new int[] { 4, 5, 6 } },
                    { "c", new int[] { 7, 8, 9 } }
                },
                str_intListMap = new Dictionary<string, List<int>>() {
                    { "a", new List<int>() { 1, 2, 3 } },
                    { "b", new List<int>() { 4, 5, 6 } },
                    { "c", new List<int>() { 7, 8, 9 } }
                },
                str_mapMap = new Dictionary<string, Dictionary<string, string>>() {
                    { "A", new Dictionary<string, string>() { { "a", "aa" }, { "b", "bb" }, { "c", "cc" } } },
                    { "B", new Dictionary<string, string>() { { "d", "dd" }, { "e", "ee" }, { "f", "ff" } } },
                    { "C", new Dictionary<string, string>() { { "g", "gg" }, { "h", "hh" }, { "i", "ii" } } }
                },
                str_userMap = new Dictionary<string, UserVO>() {
                    { "no.0", new UserVO(0, "user0") },
                    { "no.1", new UserVO(1, "user1") },
                    { "no.2", new UserVO(2, "user2") }
                }
            };
            byte[] bytes = LBUtil.Serialize(vo, "DictionaryTestVO");
            DictionaryTestVO _vo = LBUtil.Deserialize<DictionaryTestVO>(bytes, "DictionaryTestVO");
            Trace.WriteLine($"DictionaryTest() LBSize:{bytes.Length}");
        }

        private void NestedTest() {
            UserVO user0 = new UserVO(0, "User0");
            UserVO user1 = new UserVO(1, "User1");
            UserVO user2 = new UserVO(2, "User2");
            UserVO user3 = new UserVO(3, "User3");
            UserVO user4 = new UserVO(4, "User4");
            UserVO user5 = new UserVO(5, "User5");
            /*
            UserNodeVO node0 = new UserNodeVO(user0);
            UserNodeVO node1 = new UserNodeVO(user1);
            UserNodeVO node2 = new UserNodeVO(user2);
            UserNodeVO node3 = new UserNodeVO(user3);
            UserNodeVO node4 = new UserNodeVO(user4);
            UserNodeVO node5 = new UserNodeVO(user5);
            node0.next = node1;
            node1.next = node2;
            node2.next = node3;
            node3.next = node4;
            node4.next = node5;
            node5.prev = node4;
            node4.prev = node3;
            node3.prev = node2;
            node2.prev = node1;
            node1.prev = node0;
            */
            // 双向链表 循环嵌套引用  序列化时直接崩溃 引发异常：System.StackOverflowException
            //byte[] bytes = LBUtil.Serialize(node0, "UserNode");
            //UserNode _node0 = LBUtil.Deserialize<UserNode>(bytes, "UserNode");
            //Trace.WriteLine(_node0);
            // 双向链表 循环嵌套引用  JSON序列化时直接崩溃 引发异常：A possible object cycle was detected. This can either be due to a cycle or if the object depth is larger than the maximum allowed depth of 64. Consider using ReferenceHandler.Preserve on JsonSerializerOptions to support cycles
            //string json = JsonSerializer.Serialize(node0, jsonOptions);
            //Trace.WriteLine(json);
        }

        private void BaseTypeTest() {
            LBWriter writer = new LBWriter();
            LBReader reader = new LBReader();
            WriteBaseType(writer);
            reader.Bytes = writer.ToBytes();
            ReadBaseType(reader);
        }

        private void WriteBaseType(LBWriter writer) {
            writer.WriteBit1(true);
            writer.WriteChar('a');
            // Int
            writer.WriteInt8(22);
            writer.WriteInt8(sbyte.MinValue);
            writer.WriteInt8(sbyte.MaxValue);
            writer.WriteInt16(222);
            writer.WriteInt16(short.MinValue);
            writer.WriteInt16(short.MaxValue);
            writer.WriteInt32(2222);
            writer.WriteInt32(int.MinValue);
            writer.WriteInt32(int.MaxValue);
            writer.WriteInt64(22222);
            writer.WriteInt64(long.MinValue);
            writer.WriteInt64(long.MaxValue);
            // UInt
            writer.WriteUInt8(66);
            writer.WriteUInt8(byte.MinValue);
            writer.WriteUInt8(byte.MaxValue);
            writer.WriteUInt16(666);
            writer.WriteUInt16(ushort.MinValue);
            writer.WriteUInt16(ushort.MaxValue);
            writer.WriteUInt32(6666);
            writer.WriteUInt32(uint.MinValue);
            writer.WriteUInt32(uint.MaxValue);
            writer.WriteUInt64(66666);
            writer.WriteUInt64(ulong.MinValue);
            writer.WriteUInt64(ulong.MaxValue);
            // Float
            writer.WriteFloat8(8 / 255f);
            writer.WriteFloat8(0 / 255f);
            writer.WriteFloat8(255 / 255f);
            writer.WriteFloat16(1.23f);
            writer.WriteFloat16(float.MinValue);
            writer.WriteFloat16(float.MaxValue);
            writer.WriteFloat32(1.23456f);
            writer.WriteFloat32(float.MinValue);
            writer.WriteFloat32(float.MaxValue);
            writer.WriteFloat64(12345.1234567890d);
            writer.WriteFloat64(double.MinValue);
            writer.WriteFloat64(double.MaxValue);
            // Decimal
            writer.WriteDecimal(-12345.6789m);
            writer.WriteDecimal(decimal.MinValue);
            writer.WriteDecimal(decimal.MaxValue);
            // DateTime
            writer.WriteDateTime(DateTime.Now);
            writer.WriteDateTime(DateTime.UtcNow);
            writer.WriteDateTime(default(DateTime));
            // VInt
            writer.WriteVInt16(123);
            writer.WriteVInt16(short.MinValue);
            writer.WriteVInt16(short.MaxValue);
            writer.WriteVInt32(456);
            writer.WriteVInt32(int.MinValue);
            writer.WriteVInt32(int.MaxValue);
            writer.WriteVInt64(789);
            writer.WriteVInt64(long.MinValue);
            writer.WriteVInt64(long.MaxValue);
            // VUInt
            writer.WriteVUInt16(1234);
            writer.WriteVUInt16(ushort.MinValue);
            writer.WriteVUInt16(ushort.MaxValue);
            writer.WriteVUInt32(4567);
            writer.WriteVUInt32(uint.MinValue);
            writer.WriteVUInt32(uint.MaxValue);
            writer.WriteVUInt64(7890);
            writer.WriteVUInt64(ulong.MinValue);
            writer.WriteVUInt64(ulong.MaxValue);
            // String
            writer.WriteASCII("abc");
            writer.WriteUTF8("def");
            writer.WriteUTF16("abc一二三");
            writer.WriteUTF32("基本类型测试");
            writer.WriteVUnicode("emoji表情：" + char.ConvertFromUtf32(0x1f602)); // emoji表情(哭脸)
        }

        private void ReadBaseType(LBReader reader) {
            byte[] bytes = reader.Bytes;
            Trace.WriteLine($"BaseTypeTest LBSize:{bytes.Length}");
            bool bit1 = reader.ReadBit1();
            char c = reader.ReadChar();
            // Int
            sbyte int8_1 = reader.ReadInt8();
            sbyte int8_2 = reader.ReadInt8();
            sbyte int8_3 = reader.ReadInt8();
            short int16_1 = reader.ReadInt16();
            short int16_2 = reader.ReadInt16();
            short int16_3 = reader.ReadInt16();
            int int32_1 = reader.ReadInt32();
            int int32_2 = reader.ReadInt32();
            int int32_3 = reader.ReadInt32();
            long int64_1 = reader.ReadInt64();
            long int64_2 = reader.ReadInt64();
            long int64_3 = reader.ReadInt64();
            // UInt
            byte uint8_1 = reader.ReadUInt8();
            byte uint8_2 = reader.ReadUInt8();
            byte uint8_3 = reader.ReadUInt8();
            ushort uint16_1 = reader.ReadUInt16();
            ushort uint16_2 = reader.ReadUInt16();
            ushort uint16_3 = reader.ReadUInt16();
            uint uint32_1 = reader.ReadUInt32();
            uint uint32_2 = reader.ReadUInt32();
            uint uint32_3 = reader.ReadUInt32();
            ulong uint64_1 = reader.ReadUInt64();
            ulong uint64_2 = reader.ReadUInt64();
            ulong uint64_3 = reader.ReadUInt64();
            // Float
            float float8_1 = reader.ReadFloat8();
            float float8_2 = reader.ReadFloat8();
            float float8_3 = reader.ReadFloat8();
            float float16_1 = reader.ReadFloat16();
            float float16_2 = reader.ReadFloat16();
            float float16_3 = reader.ReadFloat16();
            float float32_1 = reader.ReadFloat32();
            float float32_2 = reader.ReadFloat32();
            float float32_3 = reader.ReadFloat32();
            double float64_1 = reader.ReadFloat64();
            double float64_2 = reader.ReadFloat64();
            double float64_3 = reader.ReadFloat64();
            // Decimal
            decimal decimal_1 = reader.ReadDecimal();
            decimal decimal_2 = reader.ReadDecimal();
            decimal decimal_3 = reader.ReadDecimal();
            // DateTime
            DateTime dateTime_1 = reader.ReadDateTime();
            DateTime dateTime_2 = reader.ReadDateTime();
            DateTime dateTime_3 = reader.ReadDateTime();
            // VInt
            short vint16_1 = reader.ReadVInt16();
            short vint16_2 = reader.ReadVInt16();
            short vint16_3 = reader.ReadVInt16();
            int vint32_1 = reader.ReadVInt32();
            int vint32_2 = reader.ReadVInt32();
            int vint32_3 = reader.ReadVInt32();
            long vint64_1 = reader.ReadVInt64();
            long vint64_2 = reader.ReadVInt64();
            long vint64_3 = reader.ReadVInt64();
            // VUInt
            ushort vuint16_1 = reader.ReadVUInt16();
            ushort vuint16_2 = reader.ReadVUInt16();
            ushort vuint16_3 = reader.ReadVUInt16();
            uint vuint32_1 = reader.ReadVUInt32();
            uint vuint32_2 = reader.ReadVUInt32();
            uint vuint32_3 = reader.ReadVUInt32();
            ulong vuint64_1 = reader.ReadVUInt64();
            ulong vuint64_2 = reader.ReadVUInt64();
            ulong vuint64_3 = reader.ReadVUInt64();
            // String
            string strASCII = reader.ReadASCII();
            string strUTF8 = reader.ReadUTF8();
            string strUTF16 = reader.ReadUTF16();
            string strUTF32 = reader.ReadUTF32();
            string strVUnicode = reader.ReadVUnicode();
            Trace.WriteLine(strVUnicode);
        }
        #endregion

    }

}
#endif
#endif